// Copyright 2018 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Code style modification
// ------------------------------------------------------------------

#include <ertps/types/DynamicPubSubType.h>
#include <ertps/types/DynamicType.h>
#include <ertps/types/DynamicTypeMember.h>
#include <ertps/types/DynamicDataFactory.h>
#include <ertps/types/DynamicData.h>
#include <ertps/types/TypeDescriptor.h>
#include <edds/rtps/common/SerializedPayload.h>
#include <edds/rtps/common/InstanceHandle.h>
#include <ecdr/Cdr.h>
#include <elog/Log.hpp>

namespace evbs {
namespace ertps {
namespace types {

DynamicPubSubType::DynamicPubSubType() : dynamic_type_(nullptr), m_keyBuffer(nullptr) {}

DynamicPubSubType::DynamicPubSubType(DynamicType_ptr pType) : dynamic_type_(std::move(pType)), m_keyBuffer(nullptr) {
    UpdateDynamicTypeInfo();
}

DynamicPubSubType::~DynamicPubSubType() {
    if (m_keyBuffer != nullptr) {
        free(m_keyBuffer);
    }
}

bool DynamicPubSubType::is_dynamic_type() {
    return true;
}

void DynamicPubSubType::CleanDynamicType() {
    dynamic_type_ = nullptr;
}

DynamicType_ptr DynamicPubSubType::GetDynamicType() const {
    return dynamic_type_;
}

ReturnCode_t DynamicPubSubType::SetDynamicType(DynamicData_ptr pData) {
    if (dynamic_type_ == nullptr) {
        dynamic_type_ = pData->get_type();
        UpdateDynamicTypeInfo();
        return ReturnCode_t::RETCODE_OK;
    } else {
        elogError(XTYPES, RetCode_t::RETCODE_BAD_PARAMETER,
                  "Error Setting the dynamic type " << getName() << ". There is already a registered type");
        return ReturnCode_t::RETCODE_BAD_PARAMETER;
    }
}

ReturnCode_t DynamicPubSubType::SetDynamicType(DynamicType_ptr pType) {
    if (dynamic_type_ == nullptr) {
        dynamic_type_ = pType;
        UpdateDynamicTypeInfo();
        return ReturnCode_t::RETCODE_OK;
    } else {
        elogError(XTYPES, RetCode_t::RETCODE_BAD_PARAMETER,
                  "Error Setting the dynamic type " << getName() << ". There is already a registered type");
        return ReturnCode_t::RETCODE_BAD_PARAMETER;
    }
}

void* DynamicPubSubType::createData() {
    return DynamicDataFactory::get_instance()->create_data(dynamic_type_);
}

void DynamicPubSubType::deleteData(void* data) {
    (void)DynamicDataFactory::get_instance()->delete_data((DynamicData*)data);
}

bool DynamicPubSubType::deserialize(vbs::SerializedPayload_t* payload, void* data) {
    vbsutil::ecdr::FastBuffer fastbuffer((char*)payload->data, payload->length);  // Object that manages the raw buffer.
    vbsutil::ecdr::Cdr deser(fastbuffer, vbsutil::ecdr::Cdr::DEFAULT_ENDIAN, vbsutil::ecdr::Cdr::XCDRv1);

    try {
        // Deserialize encapsulation.
        deser.read_encapsulation();
        payload->encapsulation = deser.endianness() == vbsutil::ecdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;
        //Deserialize the object:
        deser >> *((DynamicData*)data);
    } catch (vbsutil::ecdr::exception::Exception& e) {
        elogError(XTYPES, RetCode_t::RETCODE_ERROR, "Dynamic data '" << getName() << "' deserialize fail." << e.what());
        throw std::runtime_error("Dynamic data deserialize fail.");
        return false;
    }
    return true;
}

bool DynamicPubSubType::getKey(void* data, vbs::InstanceHandle_t* handle, bool force_md5) {
    if ((dynamic_type_ == nullptr) || (!m_isGetKeyDefined)) {
        return false;
    }
    DynamicData* pDynamicData = (DynamicData*)data;
    size_t keyBufferSize = static_cast<uint32_t>(DynamicData::getKeyMaxCdrSerializedSize(dynamic_type_));

    if (m_keyBuffer == nullptr) {
        m_keyBuffer = (unsigned char*)malloc(keyBufferSize > 16U ? keyBufferSize : 16U);
        if (m_keyBuffer == nullptr) {
            return false;
        }
        (void)memset(m_keyBuffer, 0, keyBufferSize > 16U ? keyBufferSize : 16U);
    }

    vbsutil::ecdr::FastBuffer fastbuffer((char*)m_keyBuffer, keyBufferSize);
    vbsutil::ecdr::Cdr ser(fastbuffer, vbsutil::ecdr::Cdr::DEFAULT_ENDIAN,
                           vbsutil::ecdr::Cdr::XCDRv1);  // Object that serializes the data.
    pDynamicData->serializeKey(ser);
    if (force_md5 || (keyBufferSize > 16U)) {
        m_md5.init();
        m_md5.update(m_keyBuffer, (uint32_t)ser.getSerializedDataLength());
        (void)m_md5.finalize();
        for (uint8_t i = 0U; i < 16U; ++i) {
            handle->value[i] = m_md5.digest[i];
        }
    } else {
        for (uint8_t i = 0U; i < 16U; ++i) {
            handle->value[i] = m_keyBuffer[i];
        }
    }
    return true;
}

std::function<uint32_t()> DynamicPubSubType::getSerializedSizeProvider(void* data) {
    return [data]() -> uint32_t {
        return (uint32_t)DynamicData::getCdrSerializedSize((DynamicData*)data) + 4U /*encapsulation*/;
    };
}

bool DynamicPubSubType::serialize(void* data, vbs::SerializedPayload_t* payload) {
    if (dynamic_type_ == nullptr) {
        dynamic_type_ = static_cast<DynamicData*>(data)->get_type();
        UpdateDynamicTypeInfo();
    }

    // Object that manages the raw buffer.
    vbsutil::ecdr::FastBuffer fastbuffer((char*)payload->data, payload->max_size);

    // Object that serializes the data.
    vbsutil::ecdr::Cdr ser(fastbuffer, vbsutil::ecdr::Cdr::DEFAULT_ENDIAN, vbsutil::ecdr::Cdr::XCDRv1);
    payload->encapsulation = ser.endianness() == vbsutil::ecdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE;
    if (dynamic_type_->get_descriptor()->annotation_is_mutable()) {
        ser.set_encoding_flag(vbsutil::ecdr::EncodingAlgorithmFlag::PL_CDR);
    } else {
        ser.set_encoding_flag(vbsutil::ecdr::EncodingAlgorithmFlag::PLAIN_CDR);
    }

    try {
        // Serialize encapsulation
        (void)ser.serialize_encapsulation();
        ser << *(DynamicData*)data;
    } catch (vbsutil::ecdr::exception::Exception& /*exception*/) {
        elogError(XTYPES, RetCode_t::RETCODE_ERROR, "Dynamic data '" << getName() << "' serialize fail.");
        return false;
    }

    payload->length = (uint32_t)ser.getSerializedDataLength();  // Get the serialized length
    return true;
}

void DynamicPubSubType::UpdateDynamicTypeInfo() {
    if (dynamic_type_ != nullptr) {
        m_isGetKeyDefined = dynamic_type_->key_annotation();

        std::map<MemberId, DynamicTypeMember*> membersMap;
        (void)dynamic_type_->get_all_members(membersMap);
        for (auto it = membersMap.begin(); it != membersMap.end(); ++it) {
            m_isGetKeyDefined |= it->second->key_annotation();
        }

        auto max_size = DynamicData::getMaxCdrSerializedSize(dynamic_type_) +
                        ertps::rtps::SerializedPayload_t::representation_header_size;
        if (max_size > std::numeric_limits<uint32_t>::max()) {
            m_typeSize = std::numeric_limits<uint32_t>::max();
        } else {
            m_typeSize = static_cast<uint32_t>(max_size);
        }
        setName(dynamic_type_->get_name().c_str());
    }
}

}  // namespace types
}  // namespace ertps
}  // namespace evbs
